from system.eventServer import eventServer
from system.gameServer import gameServer

import socket
import struct
import pickle
import random

class NetworkServer:
    def __init__(self):
        self.initialized = False
    
    def init(self, server, ip, port, serverAddress, serverPort):
        self.server = server
        self.ip = ip or socket.gethostname()
        self.serverAddress = serverAddress or self.ip
        self.serverPort = serverPort or 1234
        if self.server:
            self.port = self.serverPort
        else:
            self.port = port or self.serverPort + random.randint(1,1000)
        
        self.received = 0
        self.dropped = 0
        self.clients = []
        self.initialized = True
        
        print "init networkServer", self.server, self.ip, self.port, self.serverAddress, self.serverPort
        
        self.sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
        self.sock.setblocking(False)
        if self.server:
            self.sock.bind((self.ip,self.port))
        else:
            while True:
                try:
                    self.sock.bind((self.ip,self.port))
                    break
                except:
                    self.port += 1
        
        if not self.server:
            self.send("reg",self.port)
        
        eventServer.register("update",self.update)
        
    def destroy(self):
        numPackets = self.dropped + self.received
        if numPackets:
            print "packets dropped %d/%d = %f" % (self.dropped, numPackets, float(self.dropped)/float(numPackets))
        
        if not self.server:
            self.send("unreg",self.port)
        self.sock.close()
            
    def update(self, cmd, args):
        try:
            self.onRecv(self.sock.recvfrom(4))
        except:
            return
        
    def onRecv(self, args):
        ldata, laddr = args
        try:
            l = struct.unpack("!i",ldata)[0]
        except:
            self.dropped += 1
            return
        
        try:
            data, addr = self.sock.recvfrom(l)
        except:
            self.dropped += 1
            return
        
        if laddr == addr:
            self.received += 1
            
            cmd, realData = pickle.loads(data)
            realAddr = addr[0]
            
            if self.server:
                self.serverReceive(cmd,realData,realAddr)
            
            self.receive(cmd,realData,realAddr)
            
        else:
            self.dropped += 1
            
    def receive(self, cmd, data, addr):
        #print "receive", cmd, data, addr
        
        if cmd == "regack":
            print "connected to server at", addr
            
        elif cmd == "recevent":
            eventServer.pull(data[0],data[1])
    
    def serverReceive(self, cmd, data, addr):
        #print "server receive", cmd, data, addr
        
        if cmd == "reg":
            print "register client", addr, data
            
            self.clients.append((addr,data))
            self.send("regack",None,(addr,data))
            
        elif cmd == "unreg":
            if (addr,data) in self.clients:
                print "unregister client", addr, data
                self.clients.remove((addr,data))
            
        elif cmd == "event":
            for client in self.clients:
                self.send("recevent",data,client)
            self.receive("recevent",data,self.ip)
                
        elif cmd == "revent":
            self.send("recevent",data,random.choice(self.clients + [(self.ip,self.port)]))
            
        elif cmd == "sevent":
            eventServer.pull(data[0],data[1])
            
        else:
            print "unknown server packet", cmd, data, addr
        
    def send(self, cmd, data, dest = None):
        if not self.initialized:
            return False
        
        if not dest:
            dest = (self.serverAddress,self.serverPort)
        
        msg = pickle.dumps((cmd,data))
        
        #print "send", dest, msg
        
        self.sock.sendto(struct.pack("!i",len(msg)),dest)
        self.sock.sendto(msg,dest)
        
        return True
    
networkServer = NetworkServer()
